package Action.BackgroundThread;

import Bean.BookInfoEntity;
import Bean.Bookrecommend;
import Bean.LoanBookLogRecord;
import Bean.Log.SystemLogEntity;
import Bean.UserinfoEntity;
import org.hibernate.Session;
import tools.HibernateTools;
import tools.TimeTools;


import java.awt.print.Book;
import java.util.*;

public class RecommendedbooksThread extends Thread{
   public volatile static boolean sys_busy=false;//系统是否繁忙--由其他线程提供
   static Session session;//提交事务后立即再获取
    static HashMap<Integer,LinkedList<Integer>> user_books=new HashMap<>();//用户和订单数据关系
   static HashMap<Integer,HashSet<String>> user_tags=new HashMap<>();//用户和图书标签关系
   static HashMap<Integer,LinkedList<Integer>> user_recommend_books=new HashMap<>();//用户推荐图书id--目标数据存储
    static RecommendedbooksThread recommendThread=new RecommendedbooksThread();
    /**
     * 获取userid对应用户最近的图书订单的图书数据
     * @param userid
     */
    private static void  User_Book(int userid){
        LinkedList<Integer> books_id=new LinkedList<>();
        HashSet<String> classify=new HashSet<>();
        //获取用户图书id
        String query="from LoanBookLogRecord where userId="+userid+" order by loanTime desc";
        for (Object bookLogRecord : session.createQuery(query).setMaxResults(10).getResultList()) {
            books_id.addLast(((LoanBookLogRecord)bookLogRecord).getBookId());
        }
        //获取用户图书的标签
        for (Integer integer : books_id) {
            BookInfoEntity bookInfoEntity = session.get(BookInfoEntity.class, integer);
            if (bookInfoEntity==null){
                continue;//图书已经不存在
            }
            classify.add(bookInfoEntity.getBook_tag1());
            String book_tag2 = bookInfoEntity.getBook_tag2();
            if (book_tag2!=null&&book_tag2.equals("")){
                classify.add(book_tag2);
            }
        }
        user_books.put(userid, books_id);
        user_tags.put(userid, classify);
    }

    /**
     * 获取相似度高的用户
     * @param userId
     * @param max_size  限制最大用户数量
     * @return 相似用户列表
     */
    private static LinkedList<Integer> getSimilarUser(int userId,int max_size){
        LinkedList<Integer> user_book=user_books.get(userId);
        if (user_book==null){//用户订单信息过少,无法推荐
            return new LinkedList<>();
        }
        HashSet<String> user_tag=user_tags.get(userId);
        LinkedList<Integer[]> data=new LinkedList<>();
        for (Integer integer : user_books.keySet()) {
            if (integer==userId)//不给自己匹配
                continue;
            LinkedList<Integer> middle=new LinkedList<>(user_book);
            middle.retainAll(user_books.get(integer));
            int num=middle.size()*2;//相同图书数
            HashSet<String> middle1=new HashSet<>(user_tag);
            middle1.retainAll(user_tags.get(integer));
            num+=middle1.size();//相同标签数
            data.addLast(new Integer[]{integer,num});
        }

        Collections.sort(data, new Comparator<Integer[]>() {
            @Override
            public int compare(Integer[] o1, Integer[] o2) {
                return o2[1]-o1[1];
            }
        });
        LinkedList<Integer> res=new LinkedList<>();
        for (Integer[] datum : data) {
            if (datum[1]<=3||res.size()>=max_size)//用户相似度过低--用户数量已够
                break;
            res.addLast(datum[0]);
        }
        return res;
    }

    /**
     * 通过用户id获取用户推荐的图书--并写入内存
     * @param userid
     * @return
     */
    private static LinkedList<Integer> Bookrecommendations(int userid){
        //获取相似用户集合
        LinkedList<Integer> similarUser = getSimilarUser(userid, 10);
        HashMap<Integer,Integer> books=new HashMap<>();
        for (Integer integer : similarUser) {
            //获取相似用户的数据
            for (Integer integer1 : user_books.get(integer)) {
                //统计推荐图书的出现次数
                books.put(integer1, books.getOrDefault(integer1, 0)+1);
            }
        }
        //推荐出的图书排序:
        //数据预处理
        LinkedList<int[]> middle=new LinkedList<>();
        for (Integer integer : books.keySet()) {
            middle.addLast(new int[]{integer,books.get(integer)});
        }
        Collections.sort(middle, new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return o2[1]-o1[1];
            }
        });
        //数据清洗
        LinkedList<Integer> res=new LinkedList<>();
        for (int[] ints : middle) {
            res.add(ints[0]);
        }
        res.removeAll(user_books.get(userid));
        user_recommend_books.put(userid, res);
        return res;
    }

    private static boolean updateDatabase(){
        try {
            for (Map.Entry<Integer, LinkedList<Integer>> data : user_recommend_books.entrySet()) {
                Bookrecommend bookrecommend=session.get(Bookrecommend.class, data.getKey());
                if (bookrecommend==null){
                    bookrecommend=new Bookrecommend();
                    bookrecommend.setId(data.getKey());
                }
                bookrecommend.formatData(data.getValue());
                session.saveOrUpdate(bookrecommend);
            }
        }catch (Exception e){
            System.out.println("推荐图书信息写入失败");
            return false;//写入失败
        }
        return true;
    }

    private static  List<UserinfoEntity> initOrUpdate(){
        //预处理:
       List<UserinfoEntity> userinfoEntities=session. createQuery("from UserinfoEntity ").getResultList();//获取用户所有信息
        //用户数据初始化
        for (UserinfoEntity userinfoEntity : userinfoEntities) {
            //获取用户的订单的图书信息和标签
            User_Book(userinfoEntity.getId());
        }
        //寻找相似用户
//        for (UserinfoEntity userinfoEntity : userinfoEntities) {
//            getSimilarUser(userinfoEntity.getId(), 10);
//        }
        return userinfoEntities;
    }

    public static synchronized boolean starter(){//线程启动器
        try {
            if (!recommendThread.isAlive()){
                recommendThread.setName("图书借阅系统_图书推荐线程_高金磊");
                recommendThread.setPriority(1);//最低线程优先级
                recommendThread.start();
            }
        }catch (Exception e){
            return false;//没有出现过
        }
        return true;
    }
    @Override
    public void run() {

        try {
            sleep(10000);//注意此处需要推迟,否则项目启动将会缓慢
        } catch (InterruptedException e) {
        }
        session=HibernateTools.getSession();
        long altTime=0;
        while (true){
            System.err.println(TimeTools.timeStamp2Date(null, null)+"图书推荐线程正在运行");
            if (System.currentTimeMillis()-altTime<1000*60*60*6){
                //上次执行小于6H--休眠60分钟
                try {
                    sleep(1000*60*60);
                } catch (InterruptedException e) {
                }
                continue;
            }
            altTime=System.currentTimeMillis();
            //进入推荐图书的计算流程
            if (!session.getTransaction().isActive())
            {
                session=HibernateTools.getSession();
            }
            while (sys_busy){
                //系统繁忙--暂停1分钟
                sys_busy=false;//一分钟内被改回则继续繁忙
                try {
                    sleep(1000*60);
                } catch (InterruptedException e) {
                }
            }
            List<UserinfoEntity> userinfoEntities = initOrUpdate();//初始化数据
            for (UserinfoEntity userinfoEntity : userinfoEntities) {
                //对每一个用户图书推荐进行更新--系统不繁忙也要减缓操作
                do {
                    sys_busy=false;
                    try {
                        sleep(1000*60);
                    } catch (InterruptedException e) {
                    }
                }while( sys_busy);
                //获取推荐出的图书信息
                RecommendedbooksThread.Bookrecommendations(userinfoEntity.getId());
            }
            //所有用户计算完成后将数据写入数据库
            if (updateDatabase()) {
                String res="图书推荐数据更新完成,本次耗时"+(System.currentTimeMillis()-altTime)/1000/60+"分钟";
                SystemLogEntity.addLog(-1, null, res);
                System.err.println(TimeTools.timeStamp2Date(null,null)+res);
                altTime=System.currentTimeMillis();
            }

            HibernateTools.commit(session);
            session=HibernateTools.getSession();

            try {
                sleep(5000);
            } catch (InterruptedException e) {
            }

        }
    }
}
